#include "central_cache.h"
#include "page_cache.h"

#include<string.h>



CentralCache CentralCache::_sInst;

size_t CentralCache::FetRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
    size_t index = SizeClass::Index(size);
    _spanList[index]._mtx.lock();
    //LOG(DEBUG, "到spanList中获取一个非空span\n");
    Span* span = GetOneSpan(_spanList[index], size);

    assert(span);
    assert(span->_freeList);

    start = span->_freeList;
    end = start;
    size_t i = 0;
    size_t actualNum = 1;
    //LOG(DEBUG, "开始将span内的大内存切分\n");
    while (i < batchNum - 1 && Nextobj(end) != nullptr)
    {
        end = Nextobj(end);
        ++i;
        ++actualNum;
    }
    span->_freeList = Nextobj(end);
    Nextobj(end) = nullptr;
    span->_useCount += actualNum;

    _spanList[index]._mtx.unlock();

    return actualNum;
}

Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
    // 对list进行遍历，寻找一个非空span
    //LOG(DEBUG, "list中寻找一个非空span\n");
    Span* it = list.Begin();
    while (it != list.End())
    {
        if (it->_freeList != nullptr)  return it;
        it = it->_next;
    }

    list._mtx.unlock();

    // 到这里说明没有空闲span了，只能找page cache申请
    //LOG(DEBUG, "list中没有空闲span, 要进入下一层PagrCache中申请内存\n");
    PageCache::GetInstance()->_pageMtx.lock();
    Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));

    span->_isuse = true;
    span->objSzie = size;
    PageCache::GetInstance()->_pageMtx.unlock();

    // 对span进行切分
    // 计算起始地址和大块内存的大小（字节数）
    char* start = (char*)(span->_page_id << PAGE_SHIFT);
    size_t bytes = span->_n << PAGE_SHIFT;
    char* end = start + bytes;

    // 开始切分
    //LOG(DEBUG, "开始将从Pagecache获得的大内存切分\n");
    span->_freeList = (void*)start;
    start += size;
    void* tail = span->_freeList;
    while (start < end)
    {
        Nextobj(tail) = start;
        tail = start;
        start += size;
    }
    Nextobj(tail) = nullptr;

    // 将span挂到相应位置时要加锁，避免与其他线程同时访问
    list._mtx.lock();
    list.PushFront(span);
    return span;
}

void CentralCache::ReleaseListToSpans(void* start, size_t size)
{
    size_t index = SizeClass::Index(size);

    _spanList[index]._mtx.lock();
    while (start)
    {
        void* next = Nextobj(start);
        // 先通过地址获得该块内存原先的span
        Span* span = PageCache::GetInstance()->MapObjictToSpan(start);
        // 头插回span中的_freeList
        Nextobj(start) = span->_freeList;
        span->_freeList = start;
        span->_useCount--;
        // 判断span切分出去的小内存是否都还回来了，如果是就在还到PageCache去
        if (!span->_useCount)
        {
            _spanList[index].Erase(span);
            span->_freeList = nullptr;
            span->_next = nullptr;
            span->_prev = nullptr;

            _spanList[index]._mtx.unlock();

            PageCache::GetInstance()->_pageMtx.lock();
            PageCache::GetInstance()->ReleaseSpanTOPageCache(span);
            PageCache::GetInstance()->_pageMtx.unlock();

            _spanList[index]._mtx.lock();
        }

        start = next;
    }

    _spanList[index]._mtx.unlock();
}